#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

#include <linux/delay.h>
/*驱动注册的头文件，包含驱动的结构体和注册和卸载的函数*/
#include <linux/platform_device.h>
/*注册杂项设备头文件*/
#include <linux/miscdevice.h>
#include <linux/device.h>
/*注册设备节点的文件结构体*/
#include <linux/fs.h>
/*Linux中申请GPIO的头文件*/
#include <linux/gpio.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <mach/gpio.h>

#define COUNTER_MAGIC 'C'

#define IOCTL_COUNTER_RESET		_IOW	(COUNTER_MAGIC, 1, int)


#define GPIO_D_0			NUC980_PF0
#define GPIO_D_1			NUC980_PF1
#define GPIO_D_2			NUC980_PF2
#define GPIO_D_3			NUC980_PF3
#define GPIO_D_4			NUC980_PF4
#define GPIO_D_5			NUC980_PF5
#define GPIO_D_6			NUC980_PF6
#define GPIO_D_7			NUC980_PF7

#define GPIO_A_0			NUC980_PE10
#define GPIO_A_1			NUC980_PE11
#define GPIO_A_2			NUC980_PE12
#define GPIO_A_3			NUC980_PF9
#define GPIO_A_4			NUC980_PF10

#define GPIO_INT			NUC980_PG12
#define GPIO_RST			NUC980_PG13
#define GPIO_RD				NUC980_PG14
#define GPIO_WR				NUC980_PG15


static int major;
static struct class		*jt_counter_class;
static struct device	*jt_counter_device;


MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Dongweibo");

char counter_buf[10];


static int cpld_read(unsigned char addr, unsigned char *buf)
{
	int temp = 0;
	int result = 0;
	if( -1 == gpio_direction_input(GPIO_D_0))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_1))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_2))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_3))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_4))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_5))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_6))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_7))	return -1;
	
	gpio_set_value(GPIO_A_0, addr & 0x01);
	gpio_set_value(GPIO_A_1, (addr>>1) & 0x01);
	gpio_set_value(GPIO_A_2, (addr>>2) & 0x01);
	gpio_set_value(GPIO_A_3, (addr>>3) & 0x01);
	gpio_set_value(GPIO_A_4, (addr>>4) & 0x01);
	
	gpio_set_value(GPIO_RD, 1);
	gpio_set_value(GPIO_RD, 0);
	udelay(20);
	gpio_set_value(GPIO_RD, 1);
	
	temp = gpio_get_value(GPIO_D_7) & 0x0001;
	//printk("D[7] = %d  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;
	
	temp = gpio_get_value(GPIO_D_6) & 0x0001;
	//printk("D[6] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;	
	
	temp = gpio_get_value(GPIO_D_5) & 0x0001;
	//printk("D[5] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;	
	
	temp = gpio_get_value(GPIO_D_4) & 0x0001;
	//printk("D[4] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;	
	
	temp = gpio_get_value(GPIO_D_3) & 0x0001;
	//printk("D[3] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;
	
	temp = gpio_get_value(GPIO_D_2) & 0x0001;
	//printk("D[2] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;
	
	temp = gpio_get_value(GPIO_D_1) & 0x0001;
	//printk("D[1] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp) << 1;
	
	temp = gpio_get_value(GPIO_D_0) & 0x0001;
	//printk("D[0] = 0x%x  ",temp);
	if(temp == -1) return -1;
	result = (result | temp);	
	//printk("result = 0x%x\n",result);
	buf[1] = (unsigned char)(result);
	
	return 0;	
	
}
static int cpld_write(unsigned char addr, char *buf)
{
	if( -1 == gpio_direction_output(GPIO_D_0, buf[0] & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_1, (buf[0]>>1) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_2, (buf[0]>>2) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_3, (buf[0]>>3) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_4, (buf[0]>>4) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_5, (buf[0]>>5) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_6, (buf[0]>>6) & 0x01))	return -1;
	if( -1 == gpio_direction_output(GPIO_D_7, (buf[0]>>7) & 0x01))	return -1;
	
	gpio_set_value(GPIO_A_0, addr & 0x01);
	gpio_set_value(GPIO_A_1, (addr>>1) & 0x01);
	gpio_set_value(GPIO_A_2, (addr>>2) & 0x01);
	gpio_set_value(GPIO_A_3, (addr>>3) & 0x01);
	gpio_set_value(GPIO_A_4, (addr>>4) & 0x01);
	
	gpio_set_value(GPIO_WR, 1);
	gpio_set_value(GPIO_WR, 0);
	udelay(20);
	gpio_set_value(GPIO_WR, 1);
	
	if( -1 == gpio_direction_input(GPIO_D_0))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_1))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_2))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_3))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_4))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_5))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_6))	return -1;
	if( -1 == gpio_direction_input(GPIO_D_7))	return -1;
	
	return 0;
	
	
	
}
/*操作函数*/
static long counter_ioctl(struct file *files, unsigned int cmd,unsigned long arg)
{
	switch(cmd)
	{
		case IOCTL_COUNTER_RESET:
			gpio_set_value(GPIO_RST, 1);
			gpio_set_value(GPIO_RST, 0);
			gpio_set_value(GPIO_RST, 1);
			return 0;
		default:
			return -1;
	}
}
static ssize_t counter_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
	// read data form counter
	if(copy_from_user(counter_buf,buf,len))	//buf[0] == address; buf[1] = data
	{
        return -EFAULT;
    }	
	if( -1 == cpld_read(counter_buf[0],counter_buf))
	{
		printk(KERN_EMERG "[M] cpld_read error\n");
		return -EFAULT;		
	}
	if(copy_to_user(buf,counter_buf,len))
	{
		printk(KERN_ALERT"copy failed\n");
		return -EFAULT;
	}
	else
	{
		return len;
	}
}

static ssize_t counter_write(struct file *filp,const char *buf,size_t len,loff_t *off)
{ 
	if(copy_from_user(counter_buf,buf,len))	//buf[0] == address; buf[1] = data
    {
        return -EFAULT;
    }
	else
	{
		if( -1 == cpld_write(counter_buf[0],&(counter_buf[1]) ) )
		{
			printk(KERN_EMERG "[M] cpld_write error\n");
			return -EFAULT;			
		}
		// write data to counter
		return len;
	}
}

/*释放函数*/
static int counter_release(struct inode *inode, struct file *file)
{
//    printk(KERN_EMERG "[M]gpio_buzzer release\n");
    return 0;
}
/*打开函数*/
static int counter_open(struct inode *inode, struct file *file)
{
//    printk(KERN_EMERG "[M]gpio_buzzer open\n");
    return 0;
}

/*ops结构体，存储相关的操作函数*/
static struct file_operations counter_ops = 
{
    .owner			= 	THIS_MODULE,
    .open			= 	counter_open,
    .release		= 	counter_release,
	.read			=	counter_read,
	.write			=	counter_write,
    .unlocked_ioctl	= 	counter_ioctl,
};
static int counter_ops_init(void)
{
	int ret;
	printk(KERN_EMERG "[M] module counter init!\n");
	
	/*GPIO_D_0*/
	ret = gpio_request(GPIO_D_0,"GPIO_D_0");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_0 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_0);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_0 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_1*/
	ret = gpio_request(GPIO_D_1,"GPIO_D_1");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_1 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_1);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_1 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_2*/
	ret = gpio_request(GPIO_D_2,"GPIO_D_2");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_2 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_2);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_2 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_3*/
	ret = gpio_request(GPIO_D_3,"GPIO_D_3");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_3 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_3);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_3 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_4*/
	ret = gpio_request(GPIO_D_4,"GPIO_D_4");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_4 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_4);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_4 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_5*/
	ret = gpio_request(GPIO_D_5,"GPIO_D_5");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_5 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_5);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_5 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_6*/
	ret = gpio_request(GPIO_D_6,"GPIO_D_6");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_6 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_6);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_6 set input failed!\n");
	    return ret;
	}
	/*GPIO_D_7*/
	ret = gpio_request(GPIO_D_7,"GPIO_D_7");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_7 requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_D_7);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_D_7 set input failed!\n");
	    return ret;
	}
	/*GPIO_A_0*/
	ret = gpio_request(GPIO_A_0,"GPIO_A_0");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_A_0 requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_A_0,0);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_A_0 set input failed!\n");
	  return ret;
	} 
	/*GPIO_A_1*/
	ret = gpio_request(GPIO_A_1,"GPIO_A_1");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_A_1 requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_A_1,0);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_A_1 set input failed!\n");
	  return ret;
	} 
	/*GPIO_A_2*/
	ret = gpio_request(GPIO_A_2,"GPIO_A_2");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_A_2 requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_A_2,0);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_A_2 set input failed!\n");
	  return ret;
	} 
	/*GPIO_A_3*/
	ret = gpio_request(GPIO_A_3,"GPIO_A_3");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_A_3 requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_A_3,0);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_A_3 set input failed!\n");
	  return ret;
	} 
	/*GPIO_A_4*/
	ret = gpio_request(GPIO_A_4,"GPIO_A_4");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_A_4 requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_A_4,0);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_A_4 set input failed!\n");
	  return ret;
	} 
	/*GPIO_RST*/
	ret = gpio_request(GPIO_RST,"GPIO_RST");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_RST requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_RST,1);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_RST set input failed!\n");
	  return ret;
	} 
	/*GPIO_WR*/
	ret = gpio_request(GPIO_WR,"GPIO_WR");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_WR requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_WR,1);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_WR set input failed!\n");
	  return ret;
	} 
	/*GPIO_RD*/
	ret = gpio_request(GPIO_RD,"GPIO_RD");
	if(ret < 0)
	{
		printk(KERN_EMERG "[M]GPIO_RD requeset failed!\n");
		return ret;
	}
	ret = gpio_direction_output(GPIO_RD,1);
	if(ret < 0)
	{
	  printk(KERN_EMERG "[M]GPIO_RD set input failed!\n");
	  return ret;
	} 
	/*GPIO_INT*/
	ret = gpio_request(GPIO_INT,"GPIO_INT");
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_INT requeset failed!\n");
	    return ret;
	}
	ret = gpio_direction_input(GPIO_INT);
	if(ret < 0)
	{
	    printk(KERN_EMERG "[M]GPIO_INT set input failed!\n");
	    return ret;
	}
	
	/* set major */	
	major = register_chrdev(0, "counter", &counter_ops);  
		
	jt_counter_class = class_create(THIS_MODULE, "counter_class");
	if (!jt_counter_class) 
	{
	    printk(KERN_INFO "[M]counter_class create fail\n");
	    return -1;
	}
	/* 此处的名字即为驱动中显示的名字 */
	jt_counter_device = device_create(jt_counter_class, NULL, MKDEV(major, 0), NULL, "counter");
	if (!jt_counter_device) 
	{
	    printk(KERN_INFO "[M]jt_counter_device create fail\n");
	    return -1;
	}
	return 0;
}
static void counter_ops_exit(void)
{
    printk(KERN_EMERG "[M]module counter exit!\n");    
    unregister_chrdev(major,"counter");  
	
    gpio_free(GPIO_D_0);
	gpio_free(GPIO_D_1);
	gpio_free(GPIO_D_2);
	gpio_free(GPIO_D_3);
	gpio_free(GPIO_D_4);
	gpio_free(GPIO_D_5);
	gpio_free(GPIO_D_6);
	gpio_free(GPIO_D_7);
	gpio_free(GPIO_A_0);
	gpio_free(GPIO_A_1);
	gpio_free(GPIO_A_2);
	gpio_free(GPIO_A_3);
	gpio_free(GPIO_A_4);
	gpio_free(GPIO_RST);
	gpio_free(GPIO_WR);
	gpio_free(GPIO_RD);
	gpio_free(GPIO_INT);	
	
    device_unregister(jt_counter_device);
    class_destroy(jt_counter_class);  
}


	
module_init(counter_ops_init);
module_exit(counter_ops_exit);


